﻿#region using...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using NsMenu = Infusion.Silverlight.Controls.Menu;
#endregion

namespace Infusion.Silverlight.Controls.Ribbon
{
    #region : Ribbon :
    #region Template Parts...
    [TemplatePart(Name = Ribbon.RibbonLayoutRoot, Type = typeof(Canvas))]
    [TemplatePart(Name = Ribbon.RibbonLayoutRootClip, Type = typeof(RectangleGeometry))]
    [TemplatePart(Name = Ribbon.RibbonBody, Type = typeof(Canvas))]
    [TemplatePart(Name = Ribbon.RibbonBodyClip, Type = typeof(RectangleGeometry))]
    [TemplatePart(Name = Ribbon.RibbonBodyRectOutline, Type = typeof(Rectangle))]
    [TemplatePart(Name = Ribbon.RibbonTabs, Type = typeof(Grid))]
    [TemplatePart(Name = Ribbon.ToolTip, Type = typeof(RibbonToolTip))]
    [TemplatePart(Name = Ribbon.TitleBar, Type = typeof(Canvas))]
    [TemplatePart(Name = Ribbon.OfficeButtonLayout, Type = typeof(Grid))]
    [TemplatePart(Name = Ribbon.AnimateHighlight, Type = typeof(Storyboard))]
    [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonBorder, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButton, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonGradient0, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonGradient1, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.ColorAnimationPathOfficeButtonHighlightGradient0, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.ColorAnimationPathOfficeButtonHighlightGradient1, Type = typeof(ColorAnimation))]
    [TemplatePart(Name = Ribbon.EllipseOfficeButtonBorder, Type = typeof(Ellipse))]
    [TemplatePart(Name = Ribbon.EllipseOfficeButton, Type = typeof(Ellipse))]
    [TemplatePart(Name = Ribbon.PathOfficeButtonHighlight, Type = typeof(Path))]
    [TemplatePart(Name = Ribbon.GridQuickLaunchButtons, Type = typeof(Grid))]
    [TemplatePart(Name = Ribbon.CanvasQuickLaunchRightCap, Type = typeof(Canvas))]
    [TemplatePart(Name = Ribbon.CanvasOfficeButtonClickSurface, Type = typeof(Canvas))]
    [TemplatePart(Name = Ribbon.ImgOfficeButton, Type = typeof(Image))]
    [TemplatePart(Name = Ribbon.MainMenu, Type = typeof(RibbonMainMenu))]
    [TemplatePart(Name = Ribbon.StoryboardMenuOpacity, Type = typeof(Storyboard))]
    #endregion
    [ContentProperty("Tabs")]
    public class Ribbon : Control
    {
        #region TemplatePart Names...
        public const string RibbonLayoutRoot = "RibbonLayoutRoot";
        public const string RibbonLayoutRootClip = "RibbonLayoutRootClip";
        public const string RibbonBody = "RibbonBody";
        public const string RibbonBodyClip = "RibbonBodyClip";
        public const string RibbonBodyRectOutline = "RibbonBodyRectOutline";
        public const string RibbonTabs = "RibbonTabs";
        public const string ToolTip = "ToolTip";
        public const string TitleBar = "TitleBar";
        public const string OfficeButtonLayout = "OfficeButtonLayout";
        public const string AnimateHighlight = "AnimateHighlight";
        public const string ColorAnimationEllipseOfficeButtonBorder = "ColorAnimationEllipseOfficeButtonBorder";
        public const string ColorAnimationEllipseOfficeButton = "ColorAnimationEllipseOfficeButton";
        public const string ColorAnimationEllipseOfficeButtonGradient0 = "ColorAnimationEllipseOfficeButtonGradient0";
        public const string ColorAnimationEllipseOfficeButtonGradient1 = "ColorAnimationEllipseOfficeButtonGradient1";
        public const string ColorAnimationPathOfficeButtonHighlightGradient0 = "ColorAnimationPathOfficeButtonHighlightGradient0";
        public const string ColorAnimationPathOfficeButtonHighlightGradient1 = "ColorAnimationPathOfficeButtonHighlightGradient1";
        public const string EllipseOfficeButtonBorder = "EllipseOfficeButtonBorder";
        public const string EllipseOfficeButton = "EllipseOfficeButton";
        public const string PathOfficeButtonHighlight = "PathOfficeButtonHighlight";
        public const string GridQuickLaunchButtons = "GridQuickLaunchButtons";
        public const string CanvasQuickLaunchRightCap = "CanvasQuickLaunchRightCap";
        public const string CanvasOfficeButtonClickSurface = "CanvasOfficeButtonClickSurface";
        public const string ImgOfficeButton = "ImgOfficeButton";
        public const string MainMenu = "MainMenu";
        public const string StoryboardMenuOpacity = "StoryboardMenuOpacity";
        #endregion

        #region TemplatePart Variables...
        internal Canvas ribbonLayoutRoot;
        internal RectangleGeometry ribbonLayoutRootClip;
        internal Canvas ribbonBody;
        internal RectangleGeometry ribbonBodyClip;
        internal Rectangle ribbonBodyRectOutline;
        internal Grid ribbonTabs;
        internal RibbonToolTip toolTip;
        internal Canvas titleBar;
        internal Grid officeButtonLayout;
        internal Storyboard animateHighlight;
        internal ColorAnimation colorAnimationEllipseOfficeButtonBorder;
        internal ColorAnimation colorAnimationEllipseOfficeButton;
        internal ColorAnimation colorAnimationEllipseOfficeButtonGradient0;
        internal ColorAnimation colorAnimationEllipseOfficeButtonGradient1;
        internal ColorAnimation colorAnimationPathOfficeButtonHighlightGradient0;
        internal ColorAnimation colorAnimationPathOfficeButtonHighlightGradient1;
        internal Ellipse ellipseOfficeButtonBorder;
        internal Ellipse ellipseOfficeButton;
        internal Path pathOfficeButtonHighlight;
        internal Grid gridQuickLaunchButtons;
        internal Canvas canvasQuickLaunchRightCap;
        internal Canvas canvasOfficeButtonClickSurface;
        internal Image imgOfficeButton;
        internal RibbonMainMenu mainMenu;
        internal Storyboard storyboardMenuOpacity;
        #endregion

        #region Class Variables...
        private bool hasLoaded = false;
        private bool templateApplied = false;
        private bool officeButtonClicked = false;
        private NsMenu.Menu openMenu = null;
        private Dictionary<RibbonTab, Grid> tabContentDictionary;
        #endregion

        #region Dependency Properties...
        #region TabsProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.Tabs dependency property.
        /// </summary>
        public static readonly DependencyProperty TabsProperty = DependencyProperty.Register(
            "Tabs",
            typeof(RibbonTabCollection),
            typeof(Ribbon),
            null);
        #endregion

        #region ToolTipsProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.ToolTips dependency property.
        /// </summary>
        public static readonly DependencyProperty ToolTipsProperty = DependencyProperty.Register(
            "ToolTips",
            typeof(ToolTipCollection),
            typeof(Ribbon),
            null);
        #endregion

        #region MenusProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.Menus dependency property.
        /// </summary>
        public static readonly DependencyProperty MenusProperty = DependencyProperty.Register(
            "Menus",
            typeof(MenuCollection),
            typeof(Ribbon),
            null);
        #endregion

        #region SelectedTabIndexProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.SelectedTabIndex dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedTabIndexProperty = DependencyProperty.Register(
            "SelectedTabIndex",
            typeof(int),
            typeof(Ribbon),
            new PropertyMetadata(new PropertyChangedCallback(SelectedTabIndexPropertyChanged)));
        #endregion

        #region OfficeButtonImageSourceProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.OfficeButtonImageSource dependency property.
        /// </summary>
        public static readonly DependencyProperty OfficeButtonImageSourceProperty = DependencyProperty.Register(
            "OfficeButtonImageSource",
            typeof(string),
            typeof(Ribbon),
            null);
        #endregion

        #region QuickLaunchButtonsProperty
        /// <summary>
        /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.QuickLaunchButtons dependency property.
        /// </summary>
        public static readonly DependencyProperty QuickLaunchButtonsProperty = DependencyProperty.Register(
            "QuickLaunchButtons",
            typeof(QuickLaunchButtonCollection),
            typeof(Ribbon),
            null);
        #endregion
        #endregion

        #region Dependency Property Changed Handlers...
        #region SelectedTabIndexPropertyChanged
        private static void SelectedTabIndexPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != null)
            {
                Ribbon ribbon = (Ribbon)obj;

                if (ribbon.hasLoaded)
                {
                    int oldIndex = (int)args.OldValue;
                    int newIndex = (int)args.NewValue;

                    if (newIndex >= 0 && newIndex < ribbon.Tabs.Count)
                    {
                        if (oldIndex >= 0 && oldIndex < ribbon.Tabs.Count)
                        {
                            RibbonTab oldSelected = (RibbonTab)ribbon.Tabs[oldIndex];
                            oldSelected.Selected = false;
                        }

                        RibbonTab newSelected = (RibbonTab)ribbon.Tabs[newIndex];
                        newSelected.Selected = true;
                    }                    
                }
            }
        }
        #endregion
        #endregion

        #region Constructor(s)...
        #region Ribbon
        public Ribbon() : base()
        {
            DefaultStyleKey = typeof(Ribbon);
            IsTabStop = true;

            Tabs = new RibbonTabCollection();
            Tabs.CollectionChanged += new NotifyCollectionChangedEventHandler(Tabs_CollectionChanged);

            Menus = new MenuCollection();
            Menus.CollectionChanged += new NotifyCollectionChangedEventHandler(Menus_CollectionChanged);

            QuickLaunchButtons = new QuickLaunchButtonCollection();
            ToolTips = new ToolTipCollection();

            Loaded += new RoutedEventHandler(Ribbon_Loaded);

            tabContentDictionary = new Dictionary<RibbonTab, Grid>();
            SelectedTabIndex = -1;
        }
        #endregion
        #endregion

        #region Event Handlers...
        #region ribbonLayoutRoot_SizeChanged
        private void ribbonLayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.NewSize.Width > 1)
            {
                ribbonBody.Width = e.NewSize.Width - 2d;
                ribbonBodyClip.Rect = new Rect(0d, 0d, e.NewSize.Width > 1 ? e.NewSize.Width - 2d : 0d, ribbonBody.Height);

                ribbonBodyRectOutline.Width = e.NewSize.Width - 1d;
                ribbonBodyRectOutline.Height = ribbonBody.Height + 1d;
            }
        }
        #endregion

        #region Tabs_CollectionChanged
        private void Tabs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    {
                        foreach (RibbonTab tab in e.NewItems)
                        {
                            tab.ParentRibbon = this;

                            Grid groupGrid = new Grid();
                            groupGrid.SetValue(Canvas.TopProperty, 52d); //26d);
                            groupGrid.SetValue(Canvas.LeftProperty, 4d);
                            groupGrid.ColumnDefinitions.Add(Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));
                            groupGrid.RowDefinitions.Add(Helper.CreateGridRowDefinition(85d));

                            tabContentDictionary.Add(tab, groupGrid);
                        }
                        break;
                    }
                case NotifyCollectionChangedAction.Remove:
                    {
                        foreach (RibbonTab tab in e.NewItems)
                        {
                            tabContentDictionary.Remove(tab);
                        }
                        break;
                    }
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region Menus_CollectionChanged
        private void Menus_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (NsMenu.Menu menu in Menus)
                    {
                        if (ribbonLayoutRoot != null)
                            ribbonLayoutRoot.Children.Add(menu);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (NsMenu.Menu menu in Menus)
                    {
                        if (ribbonLayoutRoot != null)
                            ribbonLayoutRoot.Children.Remove(menu);
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region Ribbon_Loaded
        private void Ribbon_Loaded(object sender, RoutedEventArgs e)
        {
            hasLoaded = true;
            ((FrameworkElement)Application.Current.RootVisual).MouseLeftButtonDown += new MouseButtonEventHandler(Ribbon_MouseLeftButtonDown);
        }
        #endregion

        #region Ribbon_MouseLeftButtonDown
        private void Ribbon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (HasMenuOpen)
            {
                //List<UIElement> hits = (List<UIElement>)HitTest(e.GetPosition(null));
                List<UIElement> hits = (List<UIElement>)VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this);
                if (!hits.Contains(openMenu))
                {
                    HideOpenMenu();
                }
            }
        }
        #endregion

        #region RibbonTab_Loaded
        private void RibbonTab_Loaded(object sender, RoutedEventArgs e)
        {
            RibbonTab tab = (RibbonTab)sender;
            tab.HasLoaded = true;
            tabContentDictionary[tab].Visibility = Visibility.Collapsed;

            if (Tabs.AllLoaded)
            {
                if (SelectedTabIndex < 0 || SelectedTabIndex > Tabs.Count - 1)
                {
                    SelectedTabIndex = 0;
                }
                else
                {
                    Tabs[SelectedTabIndex].Selected = true;
                }
            }
        }
        #endregion

        #region canvasOfficeButtonClickSurface_MouseEnter
        private void canvasOfficeButtonClickSurface_MouseEnter(object sender, MouseEventArgs e)
        {
            if (!officeButtonClicked)
            {
                ellipseOfficeButtonBorder.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderHover);
                ellipseOfficeButton.Fill = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundHover);
                ellipseOfficeButton.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderHover);
                pathOfficeButtonHighlight.Fill = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightHover);
            }
        }
        #endregion

        #region canvasOfficeButtonClickSurface_MouseLeave
        private void canvasOfficeButtonClickSurface_MouseLeave(object sender, MouseEventArgs e)
        {
            if (!officeButtonClicked)
            {
                SetupOfficeButtonColorAnimation(false);
                animateHighlight.Stop();
                animateHighlight.Begin();
            }
        }
        #endregion

        #region canvasOfficeButtonClickSurface_MouseLeftButtonDown
        private void canvasOfficeButtonClickSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            ToggleOfficeButton();
        }
        #endregion

        #region gridQuickLaunchButtons_LayoutUpdated
        private void gridQuickLaunchButtons_LayoutUpdated(object sender, EventArgs e)
        {
            double columnWidthSum = 0d;

            foreach (ColumnDefinition colDef in gridQuickLaunchButtons.ColumnDefinitions)
            {
                columnWidthSum += colDef.ActualWidth;
            }

            canvasQuickLaunchRightCap.SetValue(Canvas.LeftProperty, 45d + columnWidthSum);
        }
        #endregion

        #region storyboardMenuOpacity_Completed
        private void storyboardMenuOpacity_Completed(object sender, EventArgs e)
        {
            mainMenu.Visibility = Visibility.Collapsed;
        }
        #endregion
        #endregion

        #region Methods...
        #region ToggleOfficeButton
        internal void ToggleOfficeButton()
        {
            officeButtonClicked = !officeButtonClicked;

            if (officeButtonClicked)
            {
                ellipseOfficeButtonBorder.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderClick);
                ellipseOfficeButton.Fill = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundClick);
                ellipseOfficeButton.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderClick);
                pathOfficeButtonHighlight.Fill = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightClick);

                mainMenu.Opacity = 1;
                mainMenu.Visibility = Visibility.Visible;
            }
            else
            {
                SetupOfficeButtonColorAnimation(true);
                animateHighlight.Stop();
                animateHighlight.Begin();

                storyboardMenuOpacity.Begin();
            }
        }
        #endregion

        #region SetupOfficeButtonColorAnimation
        private void SetupOfficeButtonColorAnimation(bool targetStateIsHover)
        {
            SolidColorBrush borderBrush, backgroundBorderBrush;
            LinearGradientBrush backgroundBrush;
            RadialGradientBrush highlightBrush;

            if (!targetStateIsHover)
            {
                borderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorder) as SolidColorBrush;
                backgroundBorderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorder) as SolidColorBrush;
                backgroundBrush = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackground) as LinearGradientBrush;
                highlightBrush = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlight) as RadialGradientBrush;
            }
            else
            {
                borderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderHover) as SolidColorBrush;
                backgroundBorderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderHover) as SolidColorBrush;
                backgroundBrush = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundHover) as LinearGradientBrush;
                highlightBrush = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightHover) as RadialGradientBrush;
            }
            
            colorAnimationEllipseOfficeButtonBorder.From = ((SolidColorBrush)ellipseOfficeButtonBorder.Stroke).Color;
            colorAnimationEllipseOfficeButtonBorder.To = borderBrush.Color;

            colorAnimationEllipseOfficeButton.From = ((SolidColorBrush)ellipseOfficeButton.Stroke).Color;
            colorAnimationEllipseOfficeButton.To = backgroundBorderBrush.Color;

            colorAnimationEllipseOfficeButtonGradient0.From = ((LinearGradientBrush)ellipseOfficeButton.Fill).GradientStops[0].Color;
            colorAnimationEllipseOfficeButtonGradient0.To = backgroundBrush.GradientStops[0].Color;
            colorAnimationEllipseOfficeButtonGradient1.From = ((LinearGradientBrush)ellipseOfficeButton.Fill).GradientStops[1].Color;
            colorAnimationEllipseOfficeButtonGradient1.To = backgroundBrush.GradientStops[1].Color;

            colorAnimationPathOfficeButtonHighlightGradient0.From = ((RadialGradientBrush)pathOfficeButtonHighlight.Fill).GradientStops[0].Color;
            colorAnimationPathOfficeButtonHighlightGradient0.To = highlightBrush.GradientStops[0].Color;
            colorAnimationPathOfficeButtonHighlightGradient1.From = ((RadialGradientBrush)pathOfficeButtonHighlight.Fill).GradientStops[1].Color;
            colorAnimationPathOfficeButtonHighlightGradient1.To = highlightBrush.GradientStops[1].Color;
        }
        #endregion

        #region OnApplyTemplate
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (!templateApplied)
            {
                ribbonLayoutRoot = (Canvas)GetTemplateChild(RibbonLayoutRoot);
                ribbonLayoutRootClip = (RectangleGeometry)GetTemplateChild(RibbonLayoutRootClip);
                ribbonBody = (Canvas)GetTemplateChild(RibbonBody);
                ribbonBodyClip = (RectangleGeometry)GetTemplateChild(RibbonBodyClip);
                ribbonBodyRectOutline = (Rectangle)GetTemplateChild(RibbonBodyRectOutline);
                ribbonTabs = (Grid)GetTemplateChild(RibbonTabs);
                toolTip = (RibbonToolTip)GetTemplateChild(ToolTip);

                titleBar = (Canvas)GetTemplateChild(TitleBar);
                officeButtonLayout = (Grid)GetTemplateChild(OfficeButtonLayout);
                animateHighlight = (Storyboard)GetTemplateChild(AnimateHighlight);
                colorAnimationEllipseOfficeButtonBorder = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonBorder);
                colorAnimationEllipseOfficeButton = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButton);
                colorAnimationEllipseOfficeButtonGradient0 = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonGradient0);
                colorAnimationEllipseOfficeButtonGradient1 = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonGradient1);
                colorAnimationPathOfficeButtonHighlightGradient0 = (ColorAnimation)GetTemplateChild(ColorAnimationPathOfficeButtonHighlightGradient0);
                colorAnimationPathOfficeButtonHighlightGradient1 = (ColorAnimation)GetTemplateChild(ColorAnimationPathOfficeButtonHighlightGradient1);
                ellipseOfficeButtonBorder = (Ellipse)GetTemplateChild(EllipseOfficeButtonBorder);
                ellipseOfficeButton = (Ellipse)GetTemplateChild(EllipseOfficeButton);
                pathOfficeButtonHighlight = (Path)GetTemplateChild(PathOfficeButtonHighlight);
                gridQuickLaunchButtons = (Grid)GetTemplateChild(GridQuickLaunchButtons);
                canvasQuickLaunchRightCap = (Canvas)GetTemplateChild(CanvasQuickLaunchRightCap);
                canvasOfficeButtonClickSurface = (Canvas)GetTemplateChild(CanvasOfficeButtonClickSurface);
                imgOfficeButton = (Image)GetTemplateChild(ImgOfficeButton);
                mainMenu = (RibbonMainMenu)GetTemplateChild(MainMenu);
                storyboardMenuOpacity = (Storyboard)GetTemplateChild(StoryboardMenuOpacity);

                mainMenu.Visibility = Visibility.Collapsed;
                mainMenu.SetValue(Canvas.ZIndexProperty, 99998);
                officeButtonLayout.SetValue(Canvas.ZIndexProperty, 99999);

                storyboardMenuOpacity.Completed += new EventHandler(storyboardMenuOpacity_Completed);

                ribbonLayoutRoot.SizeChanged += new SizeChangedEventHandler(ribbonLayoutRoot_SizeChanged);
                canvasOfficeButtonClickSurface.MouseEnter += new MouseEventHandler(canvasOfficeButtonClickSurface_MouseEnter);
                canvasOfficeButtonClickSurface.MouseLeave += new MouseEventHandler(canvasOfficeButtonClickSurface_MouseLeave);
                canvasOfficeButtonClickSurface.MouseLeftButtonDown += new MouseButtonEventHandler(canvasOfficeButtonClickSurface_MouseLeftButtonDown);
                gridQuickLaunchButtons.LayoutUpdated += new EventHandler(gridQuickLaunchButtons_LayoutUpdated);

                if (!string.IsNullOrEmpty(OfficeButtonImageSource))
                    imgOfficeButton.Source = new BitmapImage(new Uri(OfficeButtonImageSource, UriKind.Relative));

                //tabs...
                for (int tabIndex = 0; tabIndex < Tabs.Count; tabIndex++)
                {
                    if (ribbonTabs.ColumnDefinitions.Count - 1 < tabIndex + 2)
                        ribbonTabs.ColumnDefinitions.Insert(tabIndex + 1, Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));

                    Tabs[tabIndex].SetValue(Grid.ColumnProperty, tabIndex + 1);
                    Tabs[tabIndex].SetValue(Grid.RowProperty, 0);
                    Tabs[tabIndex].Loaded += new RoutedEventHandler(RibbonTab_Loaded);

                    ribbonTabs.Children.Insert(tabIndex + 1, Tabs[tabIndex]);
                    ribbonLayoutRoot.Children.Add(tabContentDictionary[Tabs[tabIndex]]);
                }

                ((RibbonButton)ribbonTabs.Children[0]).SetValue(Grid.ColumnProperty, Tabs.Count + 1);
                ((RibbonButton)ribbonTabs.Children[0]).ParentRibbon = this;

                //menus...
                foreach (NsMenu.Menu menu in Menus)
                {
                    ribbonLayoutRoot.Children.Add(menu);
                }

                //quick launch buttons...
                for (int quickButtonIndex = 0; quickButtonIndex < QuickLaunchButtons.Count; quickButtonIndex++)
			    {
                    if (gridQuickLaunchButtons.ColumnDefinitions.Count - 1 < quickButtonIndex)
                        gridQuickLaunchButtons.ColumnDefinitions.Add(Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));

                    QuickLaunchButtons[quickButtonIndex].ParentRibbon = this;
                    QuickLaunchButtons[quickButtonIndex].Format = ButtonFormat.Small;
                    QuickLaunchButtons[quickButtonIndex].SetValue(Grid.ColumnProperty, quickButtonIndex);

                    gridQuickLaunchButtons.Children.Add(QuickLaunchButtons[quickButtonIndex]);
                }

                templateApplied = true;
            }
        }
        #endregion

        #region HideOpenMenu
        internal void HideOpenMenu()
        {
            if (openMenu != null)
            {
                openMenu.Hide(true);
                openMenu = null;
            }
        }
        #endregion

        #region ShowMenu
        public NsMenu.Menu ShowMenu(string menuID, Point menuLocation, bool useAnimation)
        {
            foreach (NsMenu.Menu menu in Menus)
            {
                if (menuID == menu.ID)
                {
                    openMenu = menu;
                    break;
                }
            }

            if (openMenu == null)
            {
                throw new Exception(string.Format("MenuID '{0}' not found in the MenuCollection", menuID));
            }

            openMenu.Closing += new EventHandler(openMenu_Closing);

            double difference = ((FrameworkElement)Application.Current.RootVisual).ActualWidth - menuLocation.X - openMenu.RealWidth;
            openMenu.Show(useAnimation, new Point(menuLocation.X + (difference < 0 ? difference : 0), menuLocation.Y));
            return openMenu;
        }

        private void openMenu_Closing(object sender, EventArgs e)
        {
            if (openMenu != null)
                openMenu.Closing -= new EventHandler(openMenu_Closing);
            openMenu = null;
        }
        #endregion

        #region ShowToolTip
        internal void ShowToolTip(string toolTipID, Point toolTipLocation, bool isAlternative)
        {
            bool foundInCollection = false;

            foreach (ToolTip toolTipItem in ToolTips)
            {
                if (toolTipID == toolTipItem.ID)
                {
                    toolTip.Title = toolTipItem.Title;
                    toolTip.Description = toolTipItem.Description;
                    toolTip.AlternativeDescription = toolTipItem.AlternativeDescription;
                    toolTip.ShowHelp = toolTipItem.ShowHelp;
                    toolTip.ScreenTipSource = toolTipItem.ScreenTipSource;

                    foundInCollection = true;
                    break;
                }
            }

            if (!foundInCollection)
            {
                throw new Exception(string.Format("ToolTipID '{0}' not found in the ToolTipCollection", toolTipID));
            }

            double difference = ((FrameworkElement)Application.Current.RootVisual).ActualWidth - toolTipLocation.X - toolTip.ActualWidth;
            toolTip.Show(toolTipLocation.X + (difference < 0 ? difference : 0), toolTipLocation.Y, isAlternative);
        }
        #endregion

        #region HideToolTip
        internal void HideToolTip(string toolTipID)
        {
            toolTip.Hide();
        }
        #endregion
        #endregion

        #region Properties...
        #region Tabs
        public RibbonTabCollection Tabs
        {
            get { return (RibbonTabCollection)GetValue(TabsProperty); }
            set { SetValue(TabsProperty, value); }
        }
        #endregion

        #region Menus
        public MenuCollection Menus
        {
            get { return (MenuCollection)GetValue(MenusProperty); }
            set { SetValue(MenusProperty, value); }
        }
        #endregion

        #region QuickLaunchButtons
        public QuickLaunchButtonCollection QuickLaunchButtons
        {
            get { return (QuickLaunchButtonCollection)GetValue(QuickLaunchButtonsProperty); }
            set { SetValue(QuickLaunchButtonsProperty, value); }
        }
        #endregion

        #region ToolTips
        public ToolTipCollection ToolTips
        {
            get { return (ToolTipCollection)GetValue(ToolTipsProperty); }
            set { SetValue(ToolTipsProperty, value); }
        }
        #endregion

        #region OfficeButtonImageSource
        public string OfficeButtonImageSource
        {
            get { return (string)GetValue(OfficeButtonImageSourceProperty); }
            set { SetValue(OfficeButtonImageSourceProperty, value); }
        }
        #endregion

        #region TabContentDictionary
        internal Dictionary<RibbonTab, Grid> TabContentDictionary
        {
            get { return tabContentDictionary; }
        }
        #endregion

        #region SelectedTabIndex
        public int SelectedTabIndex
        {
            get { return (int)GetValue(SelectedTabIndexProperty); }
            set { SetValue(SelectedTabIndexProperty, value); }
        }
        #endregion

        #region HasMenuOpen
        public bool HasMenuOpen
        {
            get { return openMenu != null; }
        }
        #endregion
        #endregion
    }
    #endregion

    #region : RibbonTabCollection :
    public class RibbonTabCollection : ObservableCollection<RibbonTab>
    {
        #region Properties...
        #region AllLoaded
        public bool AllLoaded
        {
            get
            {
                bool allLoaded = true;

                foreach (RibbonTab tab in Items)
                {
                    if (!tab.HasLoaded)
                    {
                        allLoaded = false;
                        break;
                    }
                }

                return allLoaded;
            }
        }
        #endregion
        #endregion
    }
    #endregion

    #region : ToolTipCollection :
    public class ToolTipCollection : ObservableCollection<ToolTip>
    {
    }
    #endregion

    #region : MenuCollection :
    public class MenuCollection : ObservableCollection<NsMenu.Menu>
    {
    }
    #endregion

    #region : QuickLaunchButtonCollection :
    public class QuickLaunchButtonCollection : ObservableCollection<RibbonButton>
    {
    }
    #endregion
}